import java.util.ArrayList;

public class 反汇编器 {

    byte[] 机器码;
    int 机器码尺寸;

    protected Object[] 常量池;

    public 反汇编器(byte[] 机器码, int 机器码尺寸, Object[] 常量池) {
        this.机器码 = 机器码;
        this.机器码尺寸 = 机器码尺寸;
        this.常量池 = 常量池;
    }

    public void 反汇编() {
        System.out.println("反汇编：");
        int i = 0;
        while (i < 机器码尺寸) {
            i = 反汇编指令(i);
            System.out.println();
        }

        System.out.println();
    }

    public int 反汇编指令(int 指令指针) {
        var 字节码 = 机器码[指令指针];
        var 指令 = 字节码定义.指令集[字节码];

        var 指令名 = 指令.名称;
        System.out.printf("%04d:\t%-11s", 指令指针, 指令名);
        指令指针++;
        if (指令.操作数个数 == 0) {
            System.out.print("  ");
            return 指令指针;
        }

        var 操作数 = new ArrayList<>();
        for (int i = 0; i < 指令.操作数个数; i++) {
            var 值 = 字节码汇编器.取整数(机器码, 指令指针);
            指令指针 += 4;
            switch (指令.操作数类型[i]) {
                case 字节码定义.寄存器 -> 操作数.add("r" + 值);
                case 字节码定义.函数, 字节码定义.常量 -> 操作数.add(取常量池操作数(值));
                case 字节码定义.整数 -> 操作数.add(String.valueOf(值));
            }
        }

        for (int i = 0; i < 指令.操作数个数; i++) {
            if (i > 0) {
                System.out.println(", ");
            }
            
            System.out.print(操作数.get(i));
        }
        
        return 指令指针;
    }

    private String 取常量池操作数(int 常量池下标) {
        var 结果 = new StringBuilder();

        // 例如: #0:add()@1
        结果.append("#");
        结果.append(常量池下标);

        var 值 = 常量池[常量池下标].toString();
        if (常量池[常量池下标] instanceof String) {
            值 = "\"" + 值 + "\"";
        } else if (常量池[常量池下标] instanceof 函数符号) {
            var 符号 = (函数符号) 常量池[常量池下标];
            值 = 符号.名称 + "()@" + 符号.地址;
        }

        结果.append(":");
        结果.append(值);
        return 结果.toString();
    }

}
